#include <iostream>
#include<memory>

namespace zjx
{
    // c++98 auto_ptr
    template <typename T>
    class auto_ptr
    {
    public:
        // 构造的时候指向资源
        auto_ptr(T *ptr)
            : _ptr(ptr)
        {
        }
        // 实现拷贝构造、赋值重载
        auto_ptr(auto_ptr<T> &sp)
            : _ptr(sp._ptr)
        {
            // 管理权限转移
            sp._ptr = nullptr;
        }
        auto_ptr<T> &operator=(auto_ptr<T> &ap)
        {
            // 避免自己给自己赋值，同时也是权限转移
            if (this != &ap)
            {
                // 如果当前对象有指向的资源需要先释放
                if (_ptr)
                    delete _ptr;

                _ptr = ap._ptr;
                ap._ptr = nullptr;
            }
            return *this;
        }

        ~auto_ptr()
        {
            // 当对象的生命周期结束时，释放资源，置空 _ptr
            if (_ptr)
            {
                delete _ptr;
                _ptr = nullptr;
            }
        }

        // 像指针一样使用智能指针
        T &operator*()
        {
            return *_ptr;
        }
        T *operator->()
        {
            return _ptr;
        }

    private:
        T *_ptr;
    };

    // c++11 unique_ptr
    template <typename T>
    class unique_ptr
    {
    public:
        unique_ptr(T *ptr)
            : _ptr(ptr)
        {
            // 构造的时候指向资源
        }
        // unique_ptr 不支持拷贝构造与赋值重载，但是支持移动语义
        // 需要使用delete 将其拷贝构造函数与赋值重载禁掉
        unique_ptr(const unique_ptr<T> &sp) = delete;
        unique_ptr<T> &operator=(const unique_ptr<T> &sp) = delete;

        // 移动构造与移动赋值 --> 本质上就是移动权限
        unique_ptr(unique_ptr<T> &&sp)
            : _ptr(sp._ptr)
        {
            sp._ptr = nullptr;
        }

        unique_ptr<T> &operator=(unique_ptr<T> &&sp)
        {
            // 判断_ptr 是否指向资源，有资源需要释放
            if (_ptr)
                delete _ptr;
            _ptr = sp._ptr;
            sp._ptr = nullptr;
            return *this;
        }

        // 像指针一样只用智能指针
        T &operator*()
        {
            return *_ptr;
        }
        T *operator->()
        {
            return _ptr;
        }

        ~unique_ptr()
        {
            // 析构时释放指向的资源
            if (_ptr)
            {
                delete _ptr;
                _ptr = nullptr;
            }
        }

    private:
        T *_ptr = nullptr;
    };

    template <typename T>
    class shared_ptr
    {
    public:
        shared_ptr(T *ptr)
            : _ptr(ptr), _pcount(new int(1))
        {
            // 构造的时候指向资源
        }

        // 拷贝构造，拷贝 + 引用计数++
        shared_ptr(shared_ptr<T> &sp)
            : _ptr(sp._ptr), _pcount(sp._pcount)
        {
            (*_pcount)++; // 需要注意运算符的优先级
        }

        // 赋值重载与析构函数中的部分代码重复，集成处理一下
        void release()
        {
            if (--(*_pcount) == 0)
            {
                delete _ptr;
                delete _pcount;
                _ptr = _pcount = nullptr;
            }
        }

        // 赋值运算符重载
        shared_ptr<T> &operator=(shared_ptr<T> &sp)
        {
            // 不能自己给自己赋值
            // 只需要判断指向的资源是否相同就可以了
            if (sp._ptr != _ptr)
            {
                // 先判断原来的资源,引用计数为就释放资源，否则引用计数--
                // if (_pcount == 1)
                // {
                //     delete _ptr;
                //     // 记得释放计数器的资源
                //     delete _pcount;
                // }
                // else
                // {
                //     --(*_pcount); // 引用计数--
                // }
                release();

                // 赋值以及引用计数的处理
                _ptr = sp._ptr;
                _pcount = sp._pcount;
                ++(*_pcount);
            }

            return *this;
        }

        // 像指针一样使用智能指针
        T &operator*()
        {
            return *_ptr;
        }
        T *operator->()
        {
            return _ptr;
        }

        // 增加两个get函数，一个获取当前计数器的个数，一个获取资源
        int use_count() const
        {
            return *_pcount;
        }
        T *get() const
        {
            return _ptr;
        }
        ~shared_ptr()
        {
            // 如果有指向资源就释放并且引用计数为0
            // if (_ptr && --(*_pcount) == 0)
            // {
            // delete _ptr;
            // delete _pcount;
            // _ptr = _pcount = nullptr;
            // }

            release();
            // 如果智能指针所指向的资源是new[] 出来的就不要使用delete 来释放资源，而是应该使用delete[]
            // 并且如果资源不是new 出来的呢？那么全部断然地使用delete 就有可能会导致崩溃;
            // 倘若使用特化处理，在申请资源的时候利用特化来指定将来销毁使用delete[]，但是无法解决malloc 出来空间的问题，所以特化这种方法具有局限性
            // 此处的其中一种解决方式就是实现删除器：该定制删除器在构造的时候给，在构造时就自己显式推导、实例化
            
        }

    private:
        T *_ptr = nullptr;
        // 底层需要计数器 --> 堆上开辟空间,不能使用静态成员变量
        // 静态成员变量会让所有的shared_ptr 对象公共，显然想要的效果不是这样的；
        int *_pcount;
    };


    int main()
    {
        //关于释放资源的问题：解决方法一：特化；但是这种方法具有局限性，倘若看空间是malloc 出来的就又处理不了了
        // std::shared_ptr<zjx::Date[]> sp1(new zjx::Date[10]);

        return 0;
    }

    // 自定义类--日期类
    class Date
    {
    public:
        Date(int year = 2025, int month = 1, int day = 1)
            : _year(year), _month(month), _day(day)
        {
        }
        ~Date() {}

        // 比较大小
        bool operator<(const Date &d) const
        {
            //*this < d
            if (_year < d._year)
                return true;
            else if (_year == d._year)
            {
                if (_month < d._month)
                    return true;
                else if (_month == d._month)
                {
                    if (_day < d._day)
                        return true;
                }
            }
            return false;
        }

    private:
        int _year;
        int _month;
        int _day;
    };

    // 关于特化，分为函数模版特化与类模版特化；
    // 函数模版特化，例如：Less -> 判断左操作数是否小于右操作数
    // template <class T>
    // bool Less(T left, T right)//T可能会被推导成Date* , 那么此处比较大小就是地址在比较了，不符合我们的本意
    // {
    //     std::cout << "函数模版" << std::endl;
    //     return left < right;
    // }
    // 特化：针对某些特定的类型进行特殊化处理

    // 函数模版特化
    //  template<>
    //  bool Less<Date*>(Date* left, Date* right)
    //  {
    //      std::cout << "特化" << std::endl;
    //      return (*left) < (*right);//此处就会去调用Date 内部的重载函数
    //  }

    // 当然也可以直接实现函数，编译器会有现成吃现成；相较于函数模版与特化，函数就是现成的
     bool Less(Date* left , Date* right)
     {
         std::cout << "函数" << std::endl;
         return (*left) < (*right);
     }

    // 函数模版特化，其参数必须与原函数模版的参数一模一样；
    template <class T>
    bool Less(const T &left, const T &right) // T可能会被推导成Date* , 那么此处比较大小就是地址在比较了，不符合我们的本意
    {
        std::cout << "函数模版" << std::endl;
        return left < right;
    }

    // 特化
    template <>
    // bool Less<Date*>(const Date* & left , const Date* right) //这样的写法是错误的
    bool Less<Date *>(Date *const &left, Date *const &right)
    {
        std::cout << "特化" << std::endl;
        return (*left) < (*right); 
    }
}

namespace test
{
    //类模版特化分为全特化与偏特化
    //全特化 - 将模版参数全部进行特化
    template<class T1 , class T2>
    class Date
    {
    public:
        Date()
        {
            std::cout << "Date<T1, T2>" << std::endl;
        }
    private:
        T1 _d1;
        T2 _d2;
    };

    //全特化
    template<>
    class Date<int* , int*>
    {
    public:
        Date()
        {
            std::cout << "Date<int*,int*>" << std::endl;
        }
    private:
        int* _d1;
        int* _d2;
    };

    //偏特化：只特化一部分
    template<class T1>
    class Date<T1,int>//意味着，只要第二个参数为int 类型就会优先匹配这个特化的类模版去实例化
    {
    public:
        Date()
        {
            std::cout << "Date<T1,int>" << std::endl;
        }
    private:
        T1 _d1;
        int _d2;
    };

    //类模版的偏特化可以对类模版参数做进一步限制
    template<typename T1 , typename T2>
    class Date<T1* ,T2*>
    {
    public:
        Date()
        {
            std::cout << "Date<T1*,T2*>" << std::endl;
        }
    private:
        T1 _d1;
        T2 _d2;
    };

}

// int main()
// {
//     // int a = 10;
//     // int b = 20;
//     // float x = 1.1;
//     // float y = 0.5;
//     // zjx::Date d1(2025, 11, 3);
//     // zjx::Date d2(2025, 1, 1);

//     // std::cout << zjx::Less(a, b) << std::endl;
//     // std::cout << zjx::Less(x, y) << std::endl;
//     // std::cout << zjx::Less(d1, d2) << std::endl;
//     // std::cout << zjx::Less(new zjx::Date(2025, 10, 1), new zjx::Date(2004, 1, 1)) << std::endl;

//     test::Date<int,int> d3;//匹配偏特化而非原类模版；编译器会匹配最匹配的
//     test::Date<int*,int*> d4;//匹配全特化
//     test::Date<int*,char*> d5;
//     test::Date<char,int> d6;
//     test::Date<char,char> d7;

//     return 0;
// }